﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Threading;

namespace SchulteGrid
{
    public class MainWindowViewModel : INotifyPropertyChanged, IDataErrorInfo
    {
        private const string stateStart = "开始";
        private const string stateEnd = "结束";

        private int currentNumber;
        private bool isStarted;
        private DateTime startTime;
        private DispatcherTimer timer;
        private TimeSpan usedTime;
        private int selectedNumber;
        private int actualRowsCount;
        private int actualColumnsCount;
        private string gameState = stateEnd;

        public int SelectedNumber
        {
            get
            {
                return selectedNumber;
            }
            set
            {
                if (value != selectedNumber)
                {
                    selectedNumber = value;
                    RaisePropertyChanged("SelectedNumber");
                }
            }
        }

        public bool IsStarted
        {
            get
            {
                return isStarted;
            }
            set
            {
                if (value != isStarted)
                {
                    isStarted = value;
                    RaisePropertyChanged("IsStarted");
                }
            }
        }

        public int CurrentNumber
        {
            get
            {
                return currentNumber;
            }
            set
            {
                if (currentNumber != value)
                {
                    currentNumber = value;
                    RaisePropertyChanged("CurrentNumber");
                }
            }
        }

        public int RowsCount { get; set; } = 5;
        public int ColumnsCount { get; set; } = 5;

        public int ActualRowsCount
        {
            get
            {
                return actualRowsCount;
            }
            set
            {
                if (value != actualRowsCount)
                {
                    actualRowsCount = value;
                    RaisePropertyChanged("ActualRowsCount");
                }
            }
        }

        public int ActualColumnsCount
        {
            get
            {
                return actualColumnsCount;
            }
            set
            {
                if (value != actualColumnsCount)
                {
                    actualColumnsCount = value;
                    RaisePropertyChanged("ActualColumnsCount");
                }
            }
        }

        public string GameState
        {
            get
            {
                return gameState;
            }
            set
            {
                if (value != gameState)
                {
                    gameState = value;
                    RaisePropertyChanged("GameState");
                }
            }
        }

        public int Range
        {
            get
            {
                return ActualRowsCount * ActualColumnsCount;
            }
        }

        public ObservableCollection<int> Numbers { get; private set; }

        public TimeSpan UsedTime
        {
            get
            {
                return usedTime;
            }
            set
            {
                if (value != usedTime)
                {
                    usedTime = value;
                    RaisePropertyChanged("UsedTime");
                }
            }
        }
        
        public ICommand InitializeGrid { get; private set; }

        public string Error
        {
            get
            {
                return string.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                switch (columnName)
                {
                    case "RowsCount":
                        if (RowsCount > 10 || RowsCount < 3)
                        {
                            return "行范围：3~10";
                        }
                        break;
                    case "ColumnsCount":
                        if (ColumnsCount > 10 || ColumnsCount < 3)
                        {
                            return "列范围：3~10";
                        }
                        break;
                    default:
                        break;
                }
                return string.Empty;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindowViewModel()
        {
            Initialize();
            ResetNumbers();
            Riffle();

            InitializeCommands();
            InitializeTimer();
            InitializeUsedTime();
        }

        internal void CompareSelect()
        {
            if (SelectedNumber < 0)
            {
                return;
            }

            if (!isStarted)
            {
                StartGame();
            }

            if (CurrentNumber == SelectedNumber)
            {
                CurrentNumber++;
            }

            if (CurrentNumber > Range)
            {
                StopGame();
            }
        }

        private void Initialize()
        {
            Numbers = new ObservableCollection<int>();
        }

        private void ResetNumbers()
        {
            ActualRowsCount = RowsCount;
            ActualColumnsCount = ColumnsCount;

            Numbers.Clear();
            for (int i = 0; i < Range; i++)
            {
                Numbers.Add(i + 1);
            }
        }

        private void InitializeCommands()
        {
            InitializeGrid = new RelayCommand(  obj => DoInitializeGrid(obj),
                                                obj => CanInitializeGrid(obj));
        }

        private void DoInitializeGrid(object obj)
        {
            StopGame();
            SelectedNumber = -1;
            ResetNumbers();
            Riffle();
        }

        private bool CanInitializeGrid(object obj)
        {
            return string.IsNullOrEmpty(this["RowsCount"]) &&
                   string.IsNullOrEmpty(this["ColumnsCount"]);
        }

        private void InitializeTimer()
        {
            timer = new DispatcherTimer();
            timer.Tick += SecondTick;
            timer.Interval = new TimeSpan(0, 0, 0, 0, 1);
        }

        private void SecondTick(object sender, EventArgs e)
        {
            UsedTime = DateTime.Now - startTime;
        }

        private void InitializeUsedTime()
        {
            UsedTime = default(TimeSpan);
        }

        private void StartGame()
        {
            if (!IsStarted)
            {
                InitializeUsedTime();
                startTime = DateTime.Now;
                timer.Start();
                IsStarted = true;
                CurrentNumber = 1;
                GameState = stateStart;
            }
        }

        private void StopGame()
        {
            if (IsStarted)
            {
                timer.Stop();
                CurrentNumber = 1;
                IsStarted = false;
                GameState = stateEnd;
            }
        }

        private void Riffle()
        {
            Random rand = new Random();
            for (int i = 0; i < Range; i++)
            {
                int pos = rand.Next(i, Range);
                int temp = Numbers[i];
                Numbers[i] = Numbers[pos];
                Numbers[pos] = temp;
            }
            CurrentNumber = 1;
        }

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
